/** bcmi_frame mpi writer definition.
 *
 *  bcmi_frame mpi writer definition.
 *
 *  @author : huicong
 *  @date : 2008-10-27
 *
 */

#include "MPIWriter.h"

#include "BasicHeader.h"
#include "BCMI_Frame.h"

#include <cstdlib>
#include <cstdio>
#include <string.h>
#include <iostream>

#include "ParallelFrame.h"

namespace bcmi_frame
{

    VOID MPIWriter::initialize()
    {
#ifdef LOG_MPI_WRITER
        _myLog->log(
            H_OK,
            bcmi_frame::MPI,
            __FUNCSIG__,
            "MPIWriter constructor"
            );
#endif //#ifdef LOG_MPI_WRITER
        HRESULT hr = newArray(&_bufferPool, _maxPoolNumber);
        if (FAILED(hr))
        {
#ifdef LOG_MPI_WRITER
            _myLog->log(
                hr,
                bcmi_frame::MEMORY,
                __FUNCSIG__,
                "MPIWriter constructor require memory failed"
                );
#endif //#ifdef LOG_MPI_WRITER
            //UNDO malloc test
        }
    }

    MPIWriter::MPIWriter(IN UINT _rid, INOUT LogRecorder *_log): _remotePid(_rid), _myLog(_log)
    {  
        initialize();
    }

    MPIWriter::MPIWriter(IN UINT _rid, _IN ParallelFrame *_frame):
    _remotePid(_rid),
        _myLog(_frame->getLogRecorder())
    {
        _maxPoolNumber = _frame->getMPITransferBufferPoolNumber();
        _maxPoolSize = _frame->getMPITransferBufferPoolSize();
        initialize();
    }

    MPIWriter::~MPIWriter()
    {
        for (UINT i = 0; i < _poolNumber; ++i)
        {
            delete[] _bufferPool[i];
            _bufferPool[i] = NULL;
        }
        _poolNumber = 0;
        _poolId = -1;
        _poolPtr = -1;
        delete[] _bufferPool;
        _bufferPool = NULL;
#ifdef LOG_MPI_WRITER
        _myLog->log(
            H_OK,
            bcmi_frame::MPI,
            __FUNCSIG__,
            "MPIWriter destructor"
            );
#endif //#ifdef LOG_MPI_WRITER
        _myLog = NULL;
    }

    HRESULT MPIWriter::putBufferPool()
    {
        HRESULT hr = H_OK;

        for (UINT nid = 0; nid < _poolNumber; ++nid)
        {

            setHeadInfo(nid);

            hr = send(nid);

            if (FAILED(hr)) // send to mpi failed
            {
#ifdef LOG_MPI_WRITER
                _myLog->log(
                    hr,
                    bcmi_frame::MPI,
                    __FUNCSIG__,
                    "MPIWriter send error"
                    );
#endif //#ifdef LOG_MPI_WRITER
                return hr;
            }
        }

#ifdef LOG_MPI_WRITER
        _myLog->log(
            H_OK,
            bcmi_frame::MPI,
            __FUNCSIG__,
            "MPIWriter transfer over"
            );
#endif //#ifdef LOG_MPI_WRITER

        return hr;
        //UNFINISHED
    }

    VOID MPIWriter::setHeadInfo(IN INT nid)
    {
        if (nid + 1 == _poolNumber)
        {
            _sendFlag = _SEND_OVER;
            _realSize = _poolPtr;
        }
        else
        {
            _sendFlag = _SEND_CONTINUE;
            _realSize = _maxPoolSize;
        }
        //UNDO parameter test

        memcpy(_bufferPool[nid], &_sendFlag, sizeof(_sendFlag));
        memcpy((_bufferPool[nid] + sizeof(_sendFlag)), &_realSize, sizeof(_realSize));
        //UNFINISHED
    }

    HRESULT MPIWriter::send(IN INT nid)
    {

#ifdef DEBUG_MPI_WRITER
        printf("SEND_BUFFER : nid = %d : " , nid);
        for (UINT i = 0; i < _maxPoolSize; ++i)
        {
            BYTE v = _bufferPool[nid][i];
            printf("%2x ", v);
        }
        std::cout << std::endl;
#endif //#ifdef DEBUG_MPI_WRITER

#ifdef LOG_MPI_WRITER
        _myLog->log(
            H_OK,
            bcmi_frame::MPI,
            __FUNCSIG__,
            "MPIWriter send buffer"
            );
#endif //#ifdef LOG_MPI_WRITER

        //UNDO parameter test
        HRESULT hr = H_OK;
        MPI_Send(_bufferPool[nid],
            _maxPoolSize,
            MPI_BYTE,
            _remotePid,
            bcmi_frame::FRAME_COMM_TAG,
            MPI_COMM_WORLD);
        //UNDO Log
        return hr;
        //UNFINISHED
    }

    HRESULT MPIWriter::write(IN VOID *_buf, IN UINT _size, OUT UINT &_actualSize)
    {
        //UNDO parameter test
        HRESULT hr = H_OK;

        if (0 == _poolNumber) // no pool
        {
            _bufferPool[_poolNumber++] = new BYTE[_maxPoolSize];
            //UNDO malloc test
            _poolPtr = _BUFFER_HEAD_SIZE; //pool pointer reset
            _poolId = 0;
        }

        if (_poolId >= _poolNumber) // write more than this class can contain
        {
            //UNDO Log
            _actualSize = 0;
            return H_E_MPI_WRITER_BUFFER_LACK;
        }

        if (_size >= _maxPoolSize - _poolPtr) // this pool can not contain all data
        {
            _actualSize = _maxPoolSize - _poolPtr;
            memcpy((_bufferPool[_poolId] + _poolPtr), _buf, _actualSize);
            if (_poolNumber < _maxPoolNumber) // can get new pool
            {
                _bufferPool[_poolNumber++] = new BYTE[_maxPoolSize];
                //UNDO malloc test
                _poolPtr = _BUFFER_HEAD_SIZE; // pool pointer reset
                ++_poolId;
                UINT _tmpSize;
                write((((BYTE*)_buf) + _actualSize), _size - _actualSize, _tmpSize);
                _actualSize += _tmpSize;
            }
        }
        else
        {
            _actualSize = _size;
            memcpy((_bufferPool[_poolId] + _poolPtr), _buf, _actualSize);
            _poolPtr += _actualSize;
        }

        //UNDO
        return hr;
        //UNFINISHED
    }

    UINT MPIWriter::getRemotePid()
    {
        return _remotePid;
    }
};